
package at.fhj.ase.clientarch.client.gui;

import java.awt.Window;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.rmi.ConnectException;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.sql.SQLException;
import java.util.Properties;

import javax.rmi.ssl.SslRMIClientSocketFactory;
import javax.swing.JOptionPane;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import at.fhj.ase.clientarch.common.User;
import at.fhj.ase.clientarch.common.component.IHardwareComponent;
import at.fhj.ase.clientarch.common.component.IUserComponent;
import at.fhj.ase.clientarch.common.exception.BizException;
import at.fhj.ase.clientarch.common.util.CommonUtil;
import at.fhj.ase.clientarch.common.util.WindowUtil;

/**
 * @author cja, sfe | www.fh-joanneum.at | Client Architectures and Design
 */
public enum ClientController {
  /** The sole instance of this {@link ServerController} which equals a singleton pattern implementation */
  INSTANCE;

  private final Logger logger = LoggerFactory.getLogger(getClass().getName());

  /* The server components accessible for the whole client only through this controller */
  private IHardwareComponent hwComponent;
  private IUserComponent userComponent;
  private Properties clientConfig;

  /* The GUI components */
  private Window loginGUI;
  private Window mainGUI;

  //TODO should it be public?!
  public void startApplication() throws ConnectException {
    try {
      initRMI();
      clientConfig = fetchInitialClientConfig();
    } catch (Exception e) {
      logger.error("Could not init RMI: ", e);
      throw new ConnectException("Unable to connect to server");
    }

    startLoginGUI();
  }

  private Properties fetchInitialClientConfig() throws ConnectException {
    // the first thing we should do after established connection: lookup for the client config
    try {
      return ClientController.INSTANCE.getHwComponent().getConfig();
    } catch (RemoteException e) {
      logger.error("Could not fetch config, connection problem: ", e);
      throw new ConnectException("Network fetching client config from server");
    } catch (BizException e) {
      logger.error("Could not fetch config: ", e);
      throw new ConnectException("Could not fetch config from server due to the following error: " + e);
    }
  }

  /** Starts the {@link LoginFrame}, centers it to the screen and makes it visible */
  private void startLoginGUI() {
    loginGUI = new LoginFrame();
    loginGUI.setSize(260, 150);
    WindowUtil.centerWindow(loginGUI);

    //    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());

    loginGUI.setVisible(true);
  }

  // TODO should we really allow the maingui to have a component? although it is just in the timer-method...
  /**
   * Starts the {@link MainFrame}, centers it to the screen and makes it visible
   * 
   * @throws SQLException
   * @throws ClassNotFoundException
   */
  private void startMainGUI() throws ClassNotFoundException, SQLException {
    loginGUI.dispose(); // TODO i think this should not be done that way...

    mainGUI = new MainFrame();
    mainGUI.setSize(650, 384);
    WindowUtil.centerWindow(mainGUI);
    mainGUI.setVisible(true);
  }

  // TODO should we do that at login or already at client start? and don't make it public...
  /** Initializes the RMI by fetching the registry. Also initializes the server components (properties) */
  private void initRMI() throws RemoteException, UnknownHostException, NotBoundException {
    final Registry registry = LocateRegistry.getRegistry(InetAddress.getLocalHost().getHostName(), CommonUtil.RMI_PORT, new SslRMIClientSocketFactory());

    //TODO should be read out from config
    userComponent = (IUserComponent) registry.lookup(IUserComponent.NAME);
    hwComponent = (IHardwareComponent) registry.lookup(IHardwareComponent.NAME);
  }

  void authenticateUserStartMainGUI(final User user) throws ClassNotFoundException, SQLException {
    boolean userAuthenticated = false;
    try {
      userAuthenticated = userComponent.authenticateUser(user);
    } catch (RemoteException e) {
      JOptionPane.showMessageDialog(null, "Server not reachable", "Connection problem", JOptionPane.ERROR_MESSAGE);
      logger.error("Server not reachable: ", e);
    }

    if (userAuthenticated) {
      startMainGUI();
    } else {
      JOptionPane.showMessageDialog(null, "User and/or password wrong", "User authentication at server", JOptionPane.ERROR_MESSAGE);
    }
  }

  /** @returns the component that is responsible for hardware (e.g. CPU data, etc.) issues */
  public IHardwareComponent getHwComponent() {
    return hwComponent;
  }

  /** @returns the component that is responsible for {@link User} and authentication (e.g. login) issues */
  public IUserComponent getUserComponent() {
    return userComponent;
  }

  /** @return the client timer interval of client config using {@link CommonUtil#CFG_KEY_TIMER_INTERVAL} as key. */
  long getTimerInvertal() {
    final String timerInterval = (String) clientConfig.get(CommonUtil.CFG_KEY_TIMER_INTERVAL);
    return Long.valueOf(timerInterval);
  }
}
